<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
    <title>Android应用程序启动过程源代码分析 - 老罗的Android之旅
        - 博客频道 - CSDN.NET</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="description" content="前文简要介绍了Android应用程序的Activity的启动过程。在Android系统中，应用程序是由Activity组成的，因此，应用程序的启动过程实际上就是应用程序中的默认Activity的启动过程，本文将详细分析应用程序框架层的源代码，了解Android应用程序的启动过程。&#160;&#160; &#160; &#160; &#160;在上一篇文章Android应用程序的Activity启动过程简要介绍和学习计划中，我们举例子说明了启动A" />
    <script src="http://static.blog.csdn.net/scripts/jquery.js" type="text/javascript"></script>
    <script type="text/javascript" src="http://static.blog.csdn.net/scripts/ad.js?v=1.1"></script>
        <!--new top-->
               <link rel="stylesheet" href="http://static.csdn.net/public/common/toolbar/css/index.css">        <!--new top-->

    <link rel="Stylesheet" type="text/css" href="http://static.blog.csdn.net/skin/default/css/style.css?v=1.1" />
    <link id="RSSLink" title="RSS" type="application/rss+xml" rel="alternate" href="/Luoshengyang/rss/list" />
    <link rel="shortcut icon" href="/favicon.ico" />
    <link type="text/css" rel="stylesheet" href="http://static.blog.csdn.net/scripts/SyntaxHighlighter/styles/default.css" />
 

</head>
<body>
    
   
      <!--new top-->
    <script id="toolbar-tpl-scriptId" fixed="true" prod="blog" skin="black" src="http://static.csdn.net/public/common/toolbar/js/html.js" type="text/javascript"></script>
     <!--new top-->
    <div id="container">
        <div id="header">
    <div class="header">
        <div id="blog_title">
            <h2>
                <a href="http://blog.csdn.net/luoshengyang">老罗的Android之旅</a></h2>
            <h3>爱生活，爱Android</h3>
            <div class="clear">
            </div>
        </div>
        <div class="clear">
        </div>
    </div>
</div>
<div id="navigator">
    <div class="navigator_bg">
    </div>
    <div class="navigator">
        <ul>
                <li id="btnContents"><a href="http://blog.csdn.net/luoshengyang?viewmode=contents"><span onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_mulu'])">
                    <img src="http://static.blog.csdn.net/images/ico_list.gif">目录视图</span></a></li>
                <li id="btnView"><a href="http://blog.csdn.net/luoshengyang?viewmode=list"><span onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_zhaiyao'])">
                    <img src="http://static.blog.csdn.net/images/ico_summary.gif">摘要视图</span></a></li>
                <li id="btnRss"><a href="http://blog.csdn.net/luoshengyang/rss/list"><span onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_RSS'])">
                    <img src="http://static.blog.csdn.net/images/ico_rss.gif">订阅</span></a></li>
</ul>
    </div>
</div>
<script type="text/javascript">
    var username = "Luoshengyang";
    var _blogger = username;
    var blog_address = "http://blog.csdn.net/luoshengyang";
    var static_host = "http://static.blog.csdn.net";
    var currentUserName = "";
</script>

        <div id="body">
            <div id="main">
                <div class="main">
<div class="notice tracking-ad" data-mod='popu_3' > 


<a href="http://bss.csdn.net/cview/reg/?project_id=804&identy_id=839" target="_blank"><font color=red>“程序人生”中国软件开发者职业生涯调查
</font></a>

&nbsp;&nbsp;&nbsp;&nbsp;


<a href="http://bbs.csdn.net/topics/390724670"target="_blank">
<font color=red>CSDN社区“三八节”特别活动</font></a>
&nbsp;&nbsp;&nbsp;&nbsp;


<a href="http://www.csdn.net/article/2014-03-06/2818653"target="_blank">
<font color=blue>开发者职业生涯调查之未来</font></a>




</div>                    <link href="http://static.blog.csdn.net/css/comment1.css" type="text/css" rel="stylesheet" />
<link href="http://static.blog.csdn.net/css/style1.css" type="text/css" rel="stylesheet" />
<div id="article_details" class="details">
    <div class="article_title">
    <span class="ico ico_type_Original"></span>
    <h1>
        <span class="link_title"><a href="/luoshengyang/article/details/6689748">
        Android应用程序启动过程源代码分析
        </a></span>
    </h1>
</div>

    <div class="article_manage">
        <span class="link_categories">
        分类：
            <a href="/Luoshengyang/article/category/838604">Android</a> 
        </span>
    <span class="link_postdate">2011-08-19 00:58</span>
    <span class="link_view" title="阅读次数">80755人阅读</span>
    <span class="link_comments" title="评论次数"><a href="#comments" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_pinglun'])">评论</a>(322)</span>
    <span class="link_collect"><a href="javascript:void(0);" onclick="javascript:_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_shoucang']);collectArticle('Android应用程序启动过程源代码分析','6689748');return false;" title="收藏">收藏</a></span>
    <span class="link_report"><a href="#report"  onclick="javascript:_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_jubao']);report(6689748,2);return false;" title="举报">举报</a></span>
    
</div>
<div class="tag2box"><a href='http://www.csdn.net/tag/android' target=_blank onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_tag']);">android</a><a href='http://www.csdn.net/tag/application' target=_blank onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_tag']);">application</a><a href='http://www.csdn.net/tag/class' target=_blank onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_tag']);">class</a><a href='http://www.csdn.net/tag/null' target=_blank onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_tag']);">null</a><a href='http://www.csdn.net/tag/token' target=_blank onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_tag']);">token</a></div>

    
<div id="article_content" class="article_content">
<p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;前文简要介绍了Android应用程序的Activity的启动过程。在Android系统中，应用程序是由Activity组成的，因此，应用程序的启动过程实际上就是应用程序中的默认Activity的启动过程，本文将详细分析应用程序框架层的源代码，了解Android应用程序的启动过程。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在上一篇文章<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/6685853">Android应用程序的Activity启动过程简要介绍和学习计划</a>中，我们举例子说明了启动Android应用程序中的Activity的两种情景，其中，在手机屏幕中点击应用程序图标的情景就会引发Android应用程序中的默认Activity的启动，从而把应用程序启动起来。这种启动方式的特点是会启动一个新的进程来加载相应的Activity。这里，我们继续以这个例子为例来说明Android应用程序的启动过程，即MainActivity的启动过程。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;MainActivity的启动过程如下图所示：</p><p><img src="http://hi.csdn.net/attachment/201108/18/0_1313675675dBp4.gif" alt="" /><br /></p><p style="text-align:center"><a target=_blank target="_blank" href="http://hi.csdn.net/attachment/201108/18/0_1313675675dBp4.gif">点击查看大图</a></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;下面详细分析每一步是如何实现的。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 1. Launcher.startActivitySafely</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在Android系统中，应用程序是由Launcher启动起来的，其实，Launcher本身也是一个应用程序，其它的应用程序安装后，就会Launcher的界面上出现一个相应的图标，点击这个图标时，Launcher就会对应的应用程序启动起来。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Launcher的源代码工程在packages/apps/Launcher2目录下，负责启动其它应用程序的源代码实现在src/com/android/launcher2/Launcher.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_1_8452119"  code_snippet_id="127987" snippet_file_name="blog_20131225_1_8452119" name="code" class="java">/**
* Default launcher application.
*/
public final class Launcher extends Activity
		implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {

	......

	/**
	* Launches the intent referred by the clicked shortcut.
	*
	* @param v The view representing the clicked shortcut.
	*/
	public void onClick(View v) {
		Object tag = v.getTag();
		if (tag instanceof ShortcutInfo) {
			// Open shortcut
			final Intent intent = ((ShortcutInfo) tag).intent;
			int[] pos = new int[2];
			v.getLocationOnScreen(pos);
			intent.setSourceBounds(new Rect(pos[0], pos[1],
				pos[0] + v.getWidth(), pos[1] + v.getHeight()));
			startActivitySafely(intent, tag);
		} else if (tag instanceof FolderInfo) {
			......
		} else if (v == mHandleView) {
			......
		}
	}

	void startActivitySafely(Intent intent, Object tag) {
		intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		try {
			startActivity(intent);
		} catch (ActivityNotFoundException e) {
			......
		} catch (SecurityException e) {
			......
		}
	}

	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;回忆一下前面一篇文章<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/6685853">Android应用程序的Activity启动过程简要介绍和学习计划</a>说到的应用程序Activity，它的默认Activity是MainActivity，这里是AndroidManifest.xml文件中配置的：<p></p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_2_7863474"  code_snippet_id="127987" snippet_file_name="blog_20131225_2_7863474" name="code" class="html">&lt;activity android:name=&quot;.MainActivity&quot;  
      android:label=&quot;@string/app_name&quot;&gt;  
       &lt;intent-filter&gt;  
        &lt;action android:name=&quot;android.intent.action.MAIN&quot; /&gt;  
        &lt;category android:name=&quot;android.intent.category.LAUNCHER&quot; /&gt;  
    &lt;/intent-filter&gt;  
&lt;/activity&gt;  </pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;因此，这里的intent包含的信息为：action = &quot;android.intent.action.Main&quot;，category=&quot;android.intent.category.LAUNCHER&quot;, cmp=&quot;shy.luo.activity/.MainActivity&quot;，表示它要启动的Activity为shy.luo.activity.MainActivity。Intent.FLAG_ACTIVITY_NEW_TASK表示要在一个新的Task中启动这个Activity，注意，Task是Android系统中的概念，它不同于进程Process的概念。简单地说，一个Task是一系列Activity的集合，这个集合是以堆栈的形式来组织的，遵循后进先出的原则。事实上，Task是一个非常复杂的概念，有兴趣的读者可以到官网<a target=_blank target="_blank" href="http://developer.android.com/guide/topics/manifest/activity-element.html">http://developer.android.com/guide/topics/manifest/activity-element.html</a>查看相关的资料。这里，我们只要知道，这个MainActivity要在一个新的Task中启动就可以了。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 2. Activity.startActivity</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在Step 1中，我们看到，Launcher继承于Activity类，而Activity类实现了startActivity函数，因此，这里就调用了Activity.startActivity函数，它实现在frameworks/base/core/java/android/app/Activity.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_3_8561586"  code_snippet_id="127987" snippet_file_name="blog_20131225_3_8561586" name="code" class="java">public class Activity extends ContextThemeWrapper
		implements LayoutInflater.Factory,
		Window.Callback, KeyEvent.Callback,
		OnCreateContextMenuListener, ComponentCallbacks {

	......

	@Override
	public void startActivity(Intent intent) {
		startActivityForResult(intent, -1);
	}

	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数实现很简单，它调用startActivityForResult来进一步处理，第二个参数传入-1表示不需要这个Actvity结束后的返回结果。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 3.&nbsp;Activity.startActivityForResult</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数也是实现在frameworks/base/core/java/android/app/Activity.java文件中：<br /></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_4_6751442"  code_snippet_id="127987" snippet_file_name="blog_20131225_4_6751442" name="code" class="java">public class Activity extends ContextThemeWrapper
		implements LayoutInflater.Factory,
		Window.Callback, KeyEvent.Callback,
		OnCreateContextMenuListener, ComponentCallbacks {

	......

	public void startActivityForResult(Intent intent, int requestCode) {
		if (mParent == null) {
			Instrumentation.ActivityResult ar =
				mInstrumentation.execStartActivity(
				this, mMainThread.getApplicationThread(), mToken, this,
				intent, requestCode);
			......
		} else {
			......
		}


	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; 这里的mInstrumentation是Activity类的成员变量，它的类型是Intrumentation，定义在frameworks/base/core/java/android/app/Instrumentation.java文件中，它用来监控应用程序和系统的交互。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; 这里的mMainThread也是Activity类的成员变量，它的类型是ActivityThread，它代表的是应用程序的主线程，我们在<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/6677029">Android系统在新进程中启动自定义服务过程（startService）的原理分析</a>一文中已经介绍过了。这里通过mMainThread.getApplicationThread获得它里面的ApplicationThread成员变量，它是一个Binder对象，后面我们会看到，ActivityManagerService会使用它来和ActivityThread来进行进程间通信。这里我们需注意的是，这里的mMainThread代表的是Launcher应用程序运行的进程。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; 这里的mToken也是Activity类的成员变量，它是一个Binder对象的远程接口。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; Step 4. Instrumentation.execStartActivity<br />&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; 这个函数定义在frameworks/base/core/java/android/app/Instrumentation.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_5_6893146"  code_snippet_id="127987" snippet_file_name="blog_20131225_5_6893146" name="code" class="java">public class Instrumentation {

	......

	public ActivityResult execStartActivity(
	Context who, IBinder contextThread, IBinder token, Activity target,
	Intent intent, int requestCode) {
		IApplicationThread whoThread = (IApplicationThread) contextThread;
		if (mActivityMonitors != null) {
			......
		}
		try {
			int result = ActivityManagerNative.getDefault()
				.startActivity(whoThread, intent,
				intent.resolveTypeIfNeeded(who.getContentResolver()),
				null, 0, token, target != null ? target.mEmbeddedID : null,
				requestCode, false, false);
			......
		} catch (RemoteException e) {
		}
		return null;
	}

	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; 这里的ActivityManagerNative.getDefault返回ActivityManagerService的远程接口，即ActivityManagerProxy接口，具体可以参考<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/6677029">Android系统在新进程中启动自定义服务过程（startService）的原理分析</a>一文。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; 这里的intent.resolveTypeIfNeeded返回这个intent的MIME类型，在这个例子中，没有AndroidManifest.xml设置MainActivity的MIME类型，因此，这里返回null。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; 这里的target不为null，但是target.mEmbddedID为null，我们不用关注。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; Step 5. ActivityManagerProxy.startActivity</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; 这个函数定义在frameworks/base/core/java/android/app/ActivityManagerNative.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_6_6511462"  code_snippet_id="127987" snippet_file_name="blog_20131225_6_6511462" name="code" class="java">class ActivityManagerProxy implements IActivityManager
{

	......

	public int startActivity(IApplicationThread caller, Intent intent,
			String resolvedType, Uri[] grantedUriPermissions, int grantedMode,
			IBinder resultTo, String resultWho,
			int requestCode, boolean onlyIfNeeded,
			boolean debug) throws RemoteException {
		Parcel data = Parcel.obtain();
		Parcel reply = Parcel.obtain();
		data.writeInterfaceToken(IActivityManager.descriptor);
		data.writeStrongBinder(caller != null ? caller.asBinder() : null);
		intent.writeToParcel(data, 0);
		data.writeString(resolvedType);
		data.writeTypedArray(grantedUriPermissions, 0);
		data.writeInt(grantedMode);
		data.writeStrongBinder(resultTo);
		data.writeString(resultWho);
		data.writeInt(requestCode);
		data.writeInt(onlyIfNeeded ? 1 : 0);
		data.writeInt(debug ? 1 : 0);
		mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
		reply.readException();
		int result = reply.readInt();
		reply.recycle();
		data.recycle();
		return result;
	}

	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这里的参数比较多，我们先整理一下。从上面的调用可以知道，这里的参数resolvedType、grantedUriPermissions和resultWho均为null；参数caller为ApplicationThread类型的Binder实体；参数resultTo为一个Binder实体的远程接口，我们先不关注它；参数grantedMode为0，我们也先不关注它；参数requestCode为-1；参数onlyIfNeeded和debug均空false。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 6. ActivityManagerService.startActivity</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;上一步Step 5通过Binder驱动程序就进入到ActivityManagerService的startActivity函数来了，它定义在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_7_797624"  code_snippet_id="127987" snippet_file_name="blog_20131225_7_797624" name="code" class="java">public final class ActivityManagerService extends ActivityManagerNative
		implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {

	......

	public final int startActivity(IApplicationThread caller,
			Intent intent, String resolvedType, Uri[] grantedUriPermissions,
			int grantedMode, IBinder resultTo,
			String resultWho, int requestCode, boolean onlyIfNeeded,
			boolean debug) {
		return mMainStack.startActivityMayWait(caller, intent, resolvedType,
			grantedUriPermissions, grantedMode, resultTo, resultWho,
			requestCode, onlyIfNeeded, debug, null, null);
	}


	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这里只是简单地将操作转发给成员变量mMainStack的startActivityMayWait函数，这里的mMainStack的类型为ActivityStack。<br /><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 7.&nbsp;ActivityStack.startActivityMayWait</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_8_939328"  code_snippet_id="127987" snippet_file_name="blog_20131225_8_939328" name="code" class="java">public class ActivityStack {

	......

	final int startActivityMayWait(IApplicationThread caller,
			Intent intent, String resolvedType, Uri[] grantedUriPermissions,
			int grantedMode, IBinder resultTo,
			String resultWho, int requestCode, boolean onlyIfNeeded,
			boolean debug, WaitResult outResult, Configuration config) {

		......

		boolean componentSpecified = intent.getComponent() != null;

		// Don't modify the client's object!
		intent = new Intent(intent);

		// Collect information about the target of the Intent.
		ActivityInfo aInfo;
		try {
			ResolveInfo rInfo =
				AppGlobals.getPackageManager().resolveIntent(
				intent, resolvedType,
				PackageManager.MATCH_DEFAULT_ONLY
				| ActivityManagerService.STOCK_PM_FLAGS);
			aInfo = rInfo != null ? rInfo.activityInfo : null;
		} catch (RemoteException e) {
			......
		}

		if (aInfo != null) {
			// Store the found target back into the intent, because now that
			// we have it we never want to do this again.  For example, if the
			// user navigates back to this point in the history, we should
			// always restart the exact same activity.
			intent.setComponent(new ComponentName(
				aInfo.applicationInfo.packageName, aInfo.name));
			......
		}

		synchronized (mService) {
			int callingPid;
			int callingUid;
			if (caller == null) {
				......
			} else {
				callingPid = callingUid = -1;
			}

			mConfigWillChange = config != null
				&amp;&amp; mService.mConfiguration.diff(config) != 0;

			......

			if (mMainStack &amp;&amp; aInfo != null &amp;&amp;
				(aInfo.applicationInfo.flags&amp;ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
				  
		              ......

			}

			int res = startActivityLocked(caller, intent, resolvedType,
				grantedUriPermissions, grantedMode, aInfo,
				resultTo, resultWho, requestCode, callingPid, callingUid,
				onlyIfNeeded, componentSpecified);

			if (mConfigWillChange &amp;&amp; mMainStack) {
				......
			}

			......

			if (outResult != null) {
				......
			}

			return res;
		}

	}

	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;注意，从Step 6传下来的参数outResult和config均为null，此外，表达式(aInfo.applicationInfo.flags&amp;ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0为false，因此，这里忽略了无关代码。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;下面语句对参数intent的内容进行解析，得到MainActivity的相关信息，保存在aInfo变量中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_9_1429696"  code_snippet_id="127987" snippet_file_name="blog_20131225_9_1429696" name="code" class="java">    ActivityInfo aInfo;
    try {
	ResolveInfo rInfo =
	AppGlobals.getPackageManager().resolveIntent(
		intent, resolvedType,
		PackageManager.MATCH_DEFAULT_ONLY
		| ActivityManagerService.STOCK_PM_FLAGS);
	aInfo = rInfo != null ? rInfo.activityInfo : null;
    } catch (RemoteException e) {
		......
    }</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;解析之后，得到的aInfo.applicationInfo.packageName的值为&quot;shy.luo.activity&quot;，aInfo.name的值为&quot;shy.luo.activity.MainActivity&quot;，这是在这个实例的配置文件AndroidManifest.xml里面配置的。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;此外，函数开始的地方调用intent.getComponent()函数的返回值不为null，因此，这里的componentSpecified变量为true。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;接下去就调用startActivityLocked进一步处理了。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 8. ActivityStack.startActivityLocked</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_10_7667704"  code_snippet_id="127987" snippet_file_name="blog_20131225_10_7667704" name="code" class="java">public class ActivityStack {

	......

	final int startActivityLocked(IApplicationThread caller,
		    Intent intent, String resolvedType,
		    Uri[] grantedUriPermissions,
		    int grantedMode, ActivityInfo aInfo, IBinder resultTo,
	            String resultWho, int requestCode,
		    int callingPid, int callingUid, boolean onlyIfNeeded,
		    boolean componentSpecified) {
	        int err = START_SUCCESS;

		ProcessRecord callerApp = null;
		if (caller != null) {
			callerApp = mService.getRecordForAppLocked(caller);
			if (callerApp != null) {
				callingPid = callerApp.pid;
				callingUid = callerApp.info.uid;
			} else {
				......
			}
		}

		......

		ActivityRecord sourceRecord = null;
		ActivityRecord resultRecord = null;
		if (resultTo != null) {
			int index = indexOfTokenLocked(resultTo);
			
			......
				
			if (index &gt;= 0) {
				sourceRecord = (ActivityRecord)mHistory.get(index);
				if (requestCode &gt;= 0 &amp;&amp; !sourceRecord.finishing) {
					......
				}
			}
		}

		int launchFlags = intent.getFlags();

		if ((launchFlags&amp;Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0
			&amp;&amp; sourceRecord != null) {
			......
		}

		if (err == START_SUCCESS &amp;&amp; intent.getComponent() == null) {
			......
		}

		if (err == START_SUCCESS &amp;&amp; aInfo == null) {
			......
		}

		if (err != START_SUCCESS) {
			......
		}

		......

		ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid,
			intent, resolvedType, aInfo, mService.mConfiguration,
			resultRecord, resultWho, requestCode, componentSpecified);

		......

		return startActivityUncheckedLocked(r, sourceRecord,
			grantedUriPermissions, grantedMode, onlyIfNeeded, true);
	}


	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;从传进来的参数caller得到调用者的进程信息，并保存在callerApp变量中，这里就是Launcher应用程序的进程信息了。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;前面说过，参数resultTo是Launcher这个Activity里面的一个Binder对象，通过它可以获得Launcher这个Activity的相关信息，保存在sourceRecord变量中。<br />&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;再接下来，创建即将要启动的Activity的相关信息，并保存在r变量中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_11_6413969"  code_snippet_id="127987" snippet_file_name="blog_20131225_11_6413969" name="code" class="java">ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid,
	intent, resolvedType, aInfo, mService.mConfiguration,
	resultRecord, resultWho, requestCode, componentSpecified);</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;接着调用startActivityUncheckedLocked函数进行下一步操作。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 9. ActivityStack.startActivityUncheckedLocked</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_12_2967622"  code_snippet_id="127987" snippet_file_name="blog_20131225_12_2967622" name="code" class="java">public class ActivityStack {

	......

	final int startActivityUncheckedLocked(ActivityRecord r,
		ActivityRecord sourceRecord, Uri[] grantedUriPermissions,
		int grantedMode, boolean onlyIfNeeded, boolean doResume) {
		final Intent intent = r.intent;
		final int callingUid = r.launchedFromUid;

		int launchFlags = intent.getFlags();

		// We'll invoke onUserLeaving before onPause only if the launching
		// activity did not explicitly state that this is an automated launch.
		mUserLeaving = (launchFlags&amp;Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
		
		......

		ActivityRecord notTop = (launchFlags&amp;Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP)
			!= 0 ? r : null;

		// If the onlyIfNeeded flag is set, then we can do this if the activity
		// being launched is the same as the one making the call...  or, as
		// a special case, if we do not know the caller then we count the
		// current top activity as the caller.
		if (onlyIfNeeded) {
			......
		}

		if (sourceRecord == null) {
			......
		} else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
			......
		} else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
			|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
			......
		}

		if (r.resultTo != null &amp;&amp; (launchFlags&amp;Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
			......
		}

		boolean addingToTask = false;
		if (((launchFlags&amp;Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &amp;&amp;
			(launchFlags&amp;Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
			|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
			|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
				// If bring to front is requested, and no result is requested, and
				// we can find a task that was started with this same
				// component, then instead of launching bring that one to the front.
				if (r.resultTo == null) {
					// See if there is a task to bring to the front.  If this is
					// a SINGLE_INSTANCE activity, there can be one and only one
					// instance of it in the history, and it is always in its own
					// unique task, so we do a special search.
					ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
						? findTaskLocked(intent, r.info)
						: findActivityLocked(intent, r.info);
					if (taskTop != null) {
						......
					}
				}
		}

		......

		if (r.packageName != null) {
			// If the activity being launched is the same as the one currently
			// at the top, then we need to check if it should only be launched
			// once.
			ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
			if (top != null &amp;&amp; r.resultTo == null) {
				if (top.realActivity.equals(r.realActivity)) {
					......
				}
			}

		} else {
			......
		}

		boolean newTask = false;

		// Should this be considered a new task?
		if (r.resultTo == null &amp;&amp; !addingToTask
			&amp;&amp; (launchFlags&amp;Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
				// todo: should do better management of integers.
				mService.mCurTask++;
				if (mService.mCurTask &lt;= 0) {
					mService.mCurTask = 1;
				}
				r.task = new TaskRecord(mService.mCurTask, r.info, intent,
					(r.info.flags&amp;ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
				......
				newTask = true;
				if (mMainStack) {
					mService.addRecentTaskLocked(r.task);
				}

		} else if (sourceRecord != null) {
			......
		} else {
			......
		}

		......

		startActivityLocked(r, newTask, doResume);
		return START_SUCCESS;
	}

	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;函数首先获得intent的标志值，保存在launchFlags变量中。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个intent的标志值的位Intent.FLAG_ACTIVITY_NO_USER_ACTION没有置位，因此 ，成员变量mUserLeaving的值为true。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个intent的标志值的位Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP也没有置位，因此，变量notTop的值为null。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;由于在这个例子的AndroidManifest.xml文件中，MainActivity没有配置launchMode属值，因此，这里的r.launchMode为默认值0，表示以标准（Standard，或者称为ActivityInfo.LAUNCH_MULTIPLE）的方式来启动这个Activity。Activity的启动方式有四种，其余三种分别是ActivityInfo.LAUNCH_SINGLE_INSTANCE、ActivityInfo.LAUNCH_SINGLE_TASK和ActivityInfo.LAUNCH_SINGLE_TOP，具体可以参考官方网站<a target=_blank target="_blank" href="http://developer.android.com/reference/android/content/pm/ActivityInfo.html">http://developer.android.com/reference/android/content/pm/ActivityInfo.html</a>。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;传进来的参数r.resultTo为null，表示Launcher不需要等这个即将要启动的MainActivity的执行结果。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;由于这个intent的标志值的位Intent.FLAG_ACTIVITY_NEW_TASK被置位，而且Intent.FLAG_ACTIVITY_MULTIPLE_TASK没有置位，因此，下面的if语句会被执行：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_13_9762038"  code_snippet_id="127987" snippet_file_name="blog_20131225_13_9762038" name="code" class="java">    if (((launchFlags&amp;Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &amp;&amp;
	(launchFlags&amp;Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
	|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
	|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
		// If bring to front is requested, and no result is requested, and
		// we can find a task that was started with this same
		// component, then instead of launching bring that one to the front.
		if (r.resultTo == null) {
			// See if there is a task to bring to the front.  If this is
			// a SINGLE_INSTANCE activity, there can be one and only one
			// instance of it in the history, and it is always in its own
			// unique task, so we do a special search.
			ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
				? findTaskLocked(intent, r.info)
				: findActivityLocked(intent, r.info);
			if (taskTop != null) {
				......
			}
		}
    }</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这段代码的逻辑是查看一下，当前有没有Task可以用来执行这个Activity。由于r.launchMode的值不为ActivityInfo.LAUNCH_SINGLE_INSTANCE，因此，它通过findTaskLocked函数来查找存不存这样的Task，这里返回的结果是null，即taskTop为null，因此，需要创建一个新的Task来启动这个Activity。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;接着往下看：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_14_9347335"  code_snippet_id="127987" snippet_file_name="blog_20131225_14_9347335" name="code" class="java">    if (r.packageName != null) {
	// If the activity being launched is the same as the one currently
	// at the top, then we need to check if it should only be launched
	// once.
	ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
	if (top != null &amp;&amp; r.resultTo == null) {
		if (top.realActivity.equals(r.realActivity)) {
			......
		}
	}

    } </pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这段代码的逻辑是看一下，当前在堆栈顶端的Activity是否就是即将要启动的Activity，有些情况下，如果即将要启动的Activity就在堆栈的顶端，那么，就不会重新启动这个Activity的别一个实例了，具体可以参考官方网站<a target=_blank target="_blank" href="http://developer.android.com/reference/android/content/pm/ActivityInfo.html">http://developer.android.com/reference/android/content/pm/ActivityInfo.html</a>。现在处理堆栈顶端的Activity是Launcher，与我们即将要启动的MainActivity不是同一个Activity，因此，这里不用进一步处理上述介绍的情况。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; 执行到这里，我们知道，要在一个新的Task里面来启动这个Activity了，于是新创建一个Task：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_15_253190"  code_snippet_id="127987" snippet_file_name="blog_20131225_15_253190" name="code" class="java">   if (r.resultTo == null &amp;&amp; !addingToTask
	&amp;&amp; (launchFlags&amp;Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
	// todo: should do better management of integers.
	mService.mCurTask++;
	if (mService.mCurTask &lt;= 0) {
		mService.mCurTask = 1;
	}
	r.task = new TaskRecord(mService.mCurTask, r.info, intent,
		(r.info.flags&amp;ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
	......
	newTask = true;
	if (mMainStack) {
		mService.addRecentTaskLocked(r.task);
	}

    }</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;新建的Task保存在r.task域中，同时，添加到mService中去，这里的mService就是ActivityManagerService了。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;最后就进入startActivityLocked(r, newTask, doResume)进一步处理了。这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_16_6806843"  code_snippet_id="127987" snippet_file_name="blog_20131225_16_6806843" name="code" class="java">public class ActivityStack {

	......

	private final void startActivityLocked(ActivityRecord r, boolean newTask,
			boolean doResume) {
		final int NH = mHistory.size();

		int addPos = -1;

		if (!newTask) {
			......
		}

		// Place a new activity at top of stack, so it is next to interact
		// with the user.
		if (addPos &lt; 0) {
			addPos = NH;
		}

		// If we are not placing the new activity frontmost, we do not want
		// to deliver the onUserLeaving callback to the actual frontmost
		// activity
		if (addPos &lt; NH) {
			......
		}

		// Slot the activity into the history stack and proceed
		mHistory.add(addPos, r);
		r.inHistory = true;
		r.frontOfTask = newTask;
		r.task.numActivities++;
		if (NH &gt; 0) {
			// We want to show the starting preview window if we are
			// switching to a new task, or the next activity's process is
			// not currently running.
			......
		} else {
			// If this is the first activity, don't do any fancy animations,
			// because there is nothing for it to animate on top of.
			......
		}
		
		......

		if (doResume) {
			resumeTopActivityLocked(null);
		}
	}

	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这里的NH表示当前系统中历史任务的个数，这里肯定是大于0，因为Launcher已经跑起来了。当NH&gt;0时，并且现在要切换新任务时，要做一些任务切的界面操作，这段代码我们就不看了，这里不会影响到下面启Activity的过程，有兴趣的读取可以自己研究一下。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这里传进来的参数doResume为true，于是调用resumeTopActivityLocked进一步操作。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 10. Activity.resumeTopActivityLocked</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中：<br /></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_17_4996700"  code_snippet_id="127987" snippet_file_name="blog_20131225_17_4996700" name="code" class="java">public class ActivityStack {

	......

	/**
	* Ensure that the top activity in the stack is resumed.
	*
	* @param prev The previously resumed activity, for when in the process
	* of pausing; can be null to call from elsewhere.
	*
	* @return Returns true if something is being resumed, or false if
	* nothing happened.
	*/
	final boolean resumeTopActivityLocked(ActivityRecord prev) {
		// Find the first activity that is not finishing.
		ActivityRecord next = topRunningActivityLocked(null);

		// Remember how we'll process this pause/resume situation, and ensure
		// that the state is reset however we wind up proceeding.
		final boolean userLeaving = mUserLeaving;
		mUserLeaving = false;

		if (next == null) {
			......
		}

		next.delayedResume = false;

		// If the top activity is the resumed one, nothing to do.
		if (mResumedActivity == next &amp;&amp; next.state == ActivityState.RESUMED) {
			......
		}

		// If we are sleeping, and there is no resumed activity, and the top
		// activity is paused, well that is the state we want.
		if ((mService.mSleeping || mService.mShuttingDown)
			&amp;&amp; mLastPausedActivity == next &amp;&amp; next.state == ActivityState.PAUSED) {
			......
		}

		......

		// If we are currently pausing an activity, then don't do anything
		// until that is done.
		if (mPausingActivity != null) {
			......
		}

		......

		// We need to start pausing the current activity so the top one
		// can be resumed...
		if (mResumedActivity != null) {
			......
			startPausingLocked(userLeaving, false);
			return true;
		}

		......
	}

	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;函数先通过调用topRunningActivityLocked函数获得堆栈顶端的Activity，这里就是MainActivity了，这是在上面的Step 9设置好的，保存在next变量中。&nbsp;<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; 接下来把mUserLeaving的保存在本地变量userLeaving中，然后重新设置为false，在上面的Step 9中，mUserLeaving的值为true，因此，这里的userLeaving为true。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 这里的mResumedActivity为Launcher，因为Launcher是当前正被执行的Activity。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 当我们处理休眠状态时，mLastPausedActivity保存堆栈顶端的Activity，因为当前不是休眠状态，所以mLastPausedActivity为null。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 有了这些信息之后，下面的语句就容易理解了：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_18_9282861"  code_snippet_id="127987" snippet_file_name="blog_20131225_18_9282861" name="code" class="java">    // If the top activity is the resumed one, nothing to do.
    if (mResumedActivity == next &amp;&amp; next.state == ActivityState.RESUMED) {
	......
    }

    // If we are sleeping, and there is no resumed activity, and the top
    // activity is paused, well that is the state we want.
    if ((mService.mSleeping || mService.mShuttingDown)
	&amp;&amp; mLastPausedActivity == next &amp;&amp; next.state == ActivityState.PAUSED) {
	......
    }</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;它首先看要启动的Activity是否就是当前处理Resumed状态的Activity，如果是的话，那就什么都不用做，直接返回就可以了；否则再看一下系统当前是否休眠状态，如果是的话，再看看要启动的Activity是否就是当前处于堆栈顶端的Activity，如果是的话，也是什么都不用做。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;上面两个条件都不满足，因此，在继续往下执行之前，首先要把当处于Resumed状态的Activity推入Paused状态，然后才可以启动新的Activity。但是在将当前这个Resumed状态的Activity推入Paused状态之前，首先要看一下当前是否有Activity正在进入Pausing状态，如果有的话，当前这个Resumed状态的Activity就要稍后才能进入Paused状态了，这样就保证了所有需要进入Paused状态的Activity串行处理。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这里没有处于Pausing状态的Activity，即mPausingActivity为null，而且mResumedActivity也不为null，于是就调用startPausingLocked函数把Launcher推入Paused状态去了。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 11. ActivityStack.startPausingLocked</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_19_3884668"  code_snippet_id="127987" snippet_file_name="blog_20131225_19_3884668" name="code" class="java">public class ActivityStack {

	......

	private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {
		if (mPausingActivity != null) {
			......
		}
		ActivityRecord prev = mResumedActivity;
		if (prev == null) {
			......
		}
		......
		mResumedActivity = null;
		mPausingActivity = prev;
		mLastPausedActivity = prev;
		prev.state = ActivityState.PAUSING;
		......

		if (prev.app != null &amp;&amp; prev.app.thread != null) {
			......
			try {
				......
				prev.app.thread.schedulePauseActivity(prev, prev.finishing, userLeaving,
					prev.configChangeFlags);
				......
			} catch (Exception e) {
				......
			}
		} else {
			......
		}

		......
	
	}

	......

}</pre><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;函数首先把mResumedActivity保存在本地变量prev中。在上一步Step 10中，说到mResumedActivity就是Launcher，因此，这里把Launcher进程中的ApplicationThread对象取出来，通过它来通知Launcher这个Activity它要进入Paused状态了。当然，这里的prev.app.thread是一个ApplicationThread对象的远程接口，通过调用这个远程接口的schedulePauseActivity来通知Launcher进入Paused状态。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 参数prev.finishing表示prev所代表的Activity是否正在等待结束的Activity列表中，由于Laucher这个Activity还没结束，所以这里为false；参数prev.configChangeFlags表示哪些config发生了变化，这里我们不关心它的值。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; Step 12.&nbsp;ApplicationThreadProxy.schedulePauseActivity</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 这个函数定义在frameworks/base/core/java/android/app/ApplicationThreadNative.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_20_2074524"  code_snippet_id="127987" snippet_file_name="blog_20131225_20_2074524" name="code" class="java">class ApplicationThreadProxy implements IApplicationThread {
	
	......

	public final void schedulePauseActivity(IBinder token, boolean finished,
	boolean userLeaving, int configChanges) throws RemoteException {
		Parcel data = Parcel.obtain();
		data.writeInterfaceToken(IApplicationThread.descriptor);
		data.writeStrongBinder(token);
		data.writeInt(finished ? 1 : 0);
		data.writeInt(userLeaving ? 1 :0);
		data.writeInt(configChanges);
		mRemote.transact(SCHEDULE_PAUSE_ACTIVITY_TRANSACTION, data, null,
			IBinder.FLAG_ONEWAY);
		data.recycle();
	}

	......

}</pre><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数通过Binder进程间通信机制进入到ApplicationThread.schedulePauseActivity函数中。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 13. ApplicationThread.schedulePauseActivity</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中，它是ActivityThread的内部类：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_21_8312533"  code_snippet_id="127987" snippet_file_name="blog_20131225_21_8312533" name="code" class="java">public final class ActivityThread {
	
	......

	private final class ApplicationThread extends ApplicationThreadNative {
		
		......

		public final void schedulePauseActivity(IBinder token, boolean finished,
				boolean userLeaving, int configChanges) {
			queueOrSendMessage(
				finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
				token,
				(userLeaving ? 1 : 0),
				configChanges);
		}

		......

	}

	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这里调用的函数queueOrSendMessage是ActivityThread类的成员函数。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; 上面说到，这里的finished值为false，因此，queueOrSendMessage的第一个参数值为H.PAUSE_ACTIVITY，表示要暂停token所代表的Activity，即Launcher。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; Step 14. ActivityThread.queueOrSendMessage</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中：<br /></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_22_4550542"  code_snippet_id="127987" snippet_file_name="blog_20131225_22_4550542" name="code" class="java">public final class ActivityThread {
	
	......

	private final void queueOrSendMessage(int what, Object obj, int arg1) {
		queueOrSendMessage(what, obj, arg1, 0);
	}

	private final void queueOrSendMessage(int what, Object obj, int arg1, int arg2) {
		synchronized (this) {
			......
			Message msg = Message.obtain();
			msg.what = what;
			msg.obj = obj;
			msg.arg1 = arg1;
			msg.arg2 = arg2;
			mH.sendMessage(msg);
		}
	}

	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这里首先将相关信息组装成一个msg，然后通过mH成员变量发送出去，mH的类型是H，继承于Handler类，是ActivityThread的内部类，因此，这个消息最后由H.handleMessage来处理。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 15. H.handleMessage</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_23_6644093"  code_snippet_id="127987" snippet_file_name="blog_20131225_23_6644093" name="code" class="java">public final class ActivityThread {
	
	......

	private final class H extends Handler {

		......

		public void handleMessage(Message msg) {
			......
			switch (msg.what) {
			
			......
			
			case PAUSE_ACTIVITY:
				handlePauseActivity((IBinder)msg.obj, false, msg.arg1 != 0, msg.arg2);
				maybeSnapshot();
				break;

			......

			}
		......

	}

	......

}</pre><p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这里调用ActivityThread.handlePauseActivity进一步操作，msg.obj是一个ActivityRecord对象的引用，它代表的是Launcher这个Activity。<br />&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 16.&nbsp;ActivityThread.handlePauseActivity</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_24_7342204"  code_snippet_id="127987" snippet_file_name="blog_20131225_24_7342204" name="code" class="java">public final class ActivityThread {
	
	......

	private final void handlePauseActivity(IBinder token, boolean finished,
			boolean userLeaving, int configChanges) {

		ActivityClientRecord r = mActivities.get(token);
		if (r != null) {
			//Slog.v(TAG, &quot;userLeaving=&quot; + userLeaving + &quot; handling pause of &quot; + r);
			if (userLeaving) {
				performUserLeavingActivity(r);
			}

			r.activity.mConfigChangeFlags |= configChanges;
			Bundle state = performPauseActivity(token, finished, true);

			// Make sure any pending writes are now committed.
			QueuedWork.waitToFinish();

			// Tell the activity manager we have paused.
			try {
				ActivityManagerNative.getDefault().activityPaused(token, state);
			} catch (RemoteException ex) {
			}
		}
	}

	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; 函数首先将Binder引用token转换成ActivityRecord的远程接口ActivityClientRecord，然后做了三个事情：1. 如果userLeaving为true，则通过调用performUserLeavingActivity函数来调用Activity.onUserLeaveHint通知Activity，用户要离开它了；2. 调用performPauseActivity函数来调用Activity.onPause函数，我们知道，在Activity的生命周期中，当它要让位于其它的Activity时，系统就会调用它的onPause函数；3. 它通知ActivityManagerService，这个Activity已经进入Paused状态了，ActivityManagerService现在可以完成未竟的事情，即启动MainActivity了。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 17. ActivityManagerProxy.activityPaused</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在frameworks/base/core/java/android/app/ActivityManagerNative.java文件中：<br /></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_25_9087091"  code_snippet_id="127987" snippet_file_name="blog_20131225_25_9087091" name="code" class="java">class ActivityManagerProxy implements IActivityManager
{
	......

	public void activityPaused(IBinder token, Bundle state) throws RemoteException
	{
		Parcel data = Parcel.obtain();
		Parcel reply = Parcel.obtain();
		data.writeInterfaceToken(IActivityManager.descriptor);
		data.writeStrongBinder(token);
		data.writeBundle(state);
		mRemote.transact(ACTIVITY_PAUSED_TRANSACTION, data, reply, 0);
		reply.readException();
		data.recycle();
		reply.recycle();
	}

	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这里通过Binder进程间通信机制就进入到ActivityManagerService.activityPaused函数中去了。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 18.&nbsp;ActivityManagerService.activityPaused</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中：<br /></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_26_864998"  code_snippet_id="127987" snippet_file_name="blog_20131225_26_864998" name="code" class="java">public final class ActivityManagerService extends ActivityManagerNative
			implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
	......

	public final void activityPaused(IBinder token, Bundle icicle) {
		
		......

		final long origId = Binder.clearCallingIdentity();
		mMainStack.activityPaused(token, icicle, false);
		
		......
	}

	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; 这里，又再次进入到ActivityStack类中，执行activityPaused函数。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; Step 19.&nbsp;ActivityStack.activityPaused</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_27_1006702"  code_snippet_id="127987" snippet_file_name="blog_20131225_27_1006702" name="code" class="java">public class ActivityStack {

	......

	final void activityPaused(IBinder token, Bundle icicle, boolean timeout) {
		
		......

		ActivityRecord r = null;

		synchronized (mService) {
			int index = indexOfTokenLocked(token);
			if (index &gt;= 0) {
				r = (ActivityRecord)mHistory.get(index);
				if (!timeout) {
					r.icicle = icicle;
					r.haveState = true;
				}
				mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
				if (mPausingActivity == r) {
					r.state = ActivityState.PAUSED;
					completePauseLocked();
				} else {
					......
				}
			}
		}
	}

	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; 这里通过参数token在mHistory列表中得到ActivityRecord，从上面我们知道，这个ActivityRecord代表的是Launcher这个Activity，而我们在Step 11中，把Launcher这个Activity的信息保存在mPausingActivity中，因此，这里mPausingActivity等于r，于是，执行completePauseLocked操作。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; Step 20.&nbsp;ActivityStack.completePauseLocked</p><p>&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中：<br /></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_28_3100253"  code_snippet_id="127987" snippet_file_name="blog_20131225_28_3100253" name="code" class="java">public class ActivityStack {

	......

	private final void completePauseLocked() {
		ActivityRecord prev = mPausingActivity;
		
		......

		if (prev != null) {

			......

			mPausingActivity = null;
		}

		if (!mService.mSleeping &amp;&amp; !mService.mShuttingDown) {
			resumeTopActivityLocked(prev);
		} else {
			......
		}

		......
	}

	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;函数首先把mPausingActivity变量清空，因为现在不需要它了，然后调用resumeTopActivityLokced进一步操作，它传入的参数即为代表Launcher这个Activity的ActivityRecord。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 21.&nbsp;ActivityStack.resumeTopActivityLokced<br />&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_29_1290110"  code_snippet_id="127987" snippet_file_name="blog_20131225_29_1290110" name="code" class="java">public class ActivityStack {

	......

	final boolean resumeTopActivityLocked(ActivityRecord prev) {
		......

		// Find the first activity that is not finishing.
		ActivityRecord next = topRunningActivityLocked(null);

		// Remember how we'll process this pause/resume situation, and ensure
		// that the state is reset however we wind up proceeding.
		final boolean userLeaving = mUserLeaving;
		mUserLeaving = false;

		......

		next.delayedResume = false;

		// If the top activity is the resumed one, nothing to do.
		if (mResumedActivity == next &amp;&amp; next.state == ActivityState.RESUMED) {
			......
			return false;
		}

		// If we are sleeping, and there is no resumed activity, and the top
		// activity is paused, well that is the state we want.
		if ((mService.mSleeping || mService.mShuttingDown)
			&amp;&amp; mLastPausedActivity == next &amp;&amp; next.state == ActivityState.PAUSED) {
			......
			return false;
		}

		.......


		// We need to start pausing the current activity so the top one
		// can be resumed...
		if (mResumedActivity != null) {
			......
			return true;
		}

		......


		if (next.app != null &amp;&amp; next.app.thread != null) {
			......

		} else {
			......
			startSpecificActivityLocked(next, true, true);
		}

		return true;
	}


	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;通过上面的Step 9，我们知道，当前在堆栈顶端的Activity为我们即将要启动的MainActivity，这里通过调用topRunningActivityLocked将它取回来，保存在next变量中。之前最后一个Resumed状态的Activity，即Launcher，到了这里已经处于Paused状态了，因此，mResumedActivity为null。最后一个处于Paused状态的Activity为Launcher，因此，这里的mLastPausedActivity就为Launcher。前面我们为MainActivity创建了ActivityRecord后，它的app域一直保持为null。有了这些信息后，上面这段代码就容易理解了，它最终调用startSpecificActivityLocked进行下一步操作。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; Step 22. ActivityStack.startSpecificActivityLocked<br />&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_30_6448323"  code_snippet_id="127987" snippet_file_name="blog_20131225_30_6448323" name="code" class="java">public class ActivityStack {

	......

	private final void startSpecificActivityLocked(ActivityRecord r,
			boolean andResume, boolean checkConfig) {
		// Is this activity's application already running?
		ProcessRecord app = mService.getProcessRecordLocked(r.processName,
			r.info.applicationInfo.uid);

		......

		if (app != null &amp;&amp; app.thread != null) {
			try {
				realStartActivityLocked(r, app, andResume, checkConfig);
				return;
			} catch (RemoteException e) {
				......
			}
		}

		mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
			&quot;activity&quot;, r.intent.getComponent(), false);
	}


	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;注意，这里由于是第一次启动应用程序的Activity，所以下面语句：<br /><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_31_1290893"  code_snippet_id="127987" snippet_file_name="blog_20131225_31_1290893" name="code" class="java">ProcessRecord app = mService.getProcessRecordLocked(r.processName,
	r.info.applicationInfo.uid);</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;取回来的app为null。在Activity应用程序中的AndroidManifest.xml配置文件中，我们没有指定Application标签的process属性，系统就会默认使用package的名称，这里就是&quot;shy.luo.activity&quot;了。每一个应用程序都有自己的uid，因此，这里uid + process的组合就可以为每一个应用程序创建一个ProcessRecord。当然，我们可以配置两个应用程序具有相同的uid和package，或者在AndroidManifest.xml配置文件的application标签或者activity标签中显式指定相同的process属性值，这样，不同的应用程序也可以在同一个进程中启动。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; 函数最终执行ActivityManagerService.startProcessLocked函数进行下一步操作。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; Step 23.&nbsp;ActivityManagerService.startProcessLocked</p><p>&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中：<br /></p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_32_9480748"  code_snippet_id="127987" snippet_file_name="blog_20131225_32_9480748" name="code" class="java">public final class ActivityManagerService extends ActivityManagerNative
		implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {

	......

	final ProcessRecord startProcessLocked(String processName,
			ApplicationInfo info, boolean knownToBeDead, int intentFlags,
			String hostingType, ComponentName hostingName, boolean allowWhileBooting) {

		ProcessRecord app = getProcessRecordLocked(processName, info.uid);
		
		......

		String hostingNameStr = hostingName != null
			? hostingName.flattenToShortString() : null;

		......

		if (app == null) {
			app = new ProcessRecordLocked(null, info, processName);
			mProcessNames.put(processName, info.uid, app);
		} else {
			// If this is a new package in the process, add the package to the list
			app.addPackage(info.packageName);
		}

		......

		startProcessLocked(app, hostingType, hostingNameStr);
		return (app.pid != 0) ? app : null;
	}

	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这里再次检查是否已经有以process + uid命名的进程存在，在我们这个情景中，返回值app为null，因此，后面会创建一个ProcessRecord，并存保存在成员变量mProcessNames中，最后，调用另一个startProcessLocked函数进一步操作：<p></p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_33_8542657"  code_snippet_id="127987" snippet_file_name="blog_20131225_33_8542657" name="code" class="java">public final class ActivityManagerService extends ActivityManagerNative
		implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {

	......

	private final void startProcessLocked(ProcessRecord app,
				String hostingType, String hostingNameStr) {

		......

		try {
			int uid = app.info.uid;
			int[] gids = null;
			try {
				gids = mContext.getPackageManager().getPackageGids(
					app.info.packageName);
			} catch (PackageManager.NameNotFoundException e) {
				......
			}
			
			......

			int debugFlags = 0;
			
			......
			
			int pid = Process.start(&quot;android.app.ActivityThread&quot;,
				mSimpleProcessManagement ? app.processName : null, uid, uid,
				gids, debugFlags, null);
			
			......

		} catch (RuntimeException e) {
			
			......

		}
	}

	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这里主要是调用Process.start接口来创建一个新的进程，新的进程会导入android.app.ActivityThread类，并且执行它的main函数，这就是为什么我们前面说每一个应用程序都有一个ActivityThread实例来对应的原因。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 24. ActivityThread.main</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_34_4780666"  code_snippet_id="127987" snippet_file_name="blog_20131225_34_4780666" name="code" class="java">public final class ActivityThread {

	......

	private final void attach(boolean system) {
		......

		mSystemThread = system;
		if (!system) {

			......

			IActivityManager mgr = ActivityManagerNative.getDefault();
			try {
				mgr.attachApplication(mAppThread);
			} catch (RemoteException ex) {
			}
		} else {

			......

		}
	}

	......

	public static final void main(String[] args) {
		
		.......

		ActivityThread thread = new ActivityThread();
		thread.attach(false);

		......

		Looper.loop();

		.......

		thread.detach();
		
		......
	}
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; 这个函数在进程中创建一个ActivityThread实例，然后调用它的attach函数，接着就进入消息循环了，直到最后进程退出。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; 函数attach最终调用了ActivityManagerService的远程接口ActivityManagerProxy的attachApplication函数，传入的参数是mAppThread，这是一个ApplicationThread类型的Binder对象，它的作用是用来进行进程间通信的。</p><p>&nbsp;&nbsp; &nbsp; &nbsp;Step 25.&nbsp;ActivityManagerProxy.attachApplication</p><p>&nbsp;&nbsp; &nbsp; &nbsp;这个函数定义在frameworks/base/core/java/android/app/ActivityManagerNative.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_35_7081961"  code_snippet_id="127987" snippet_file_name="blog_20131225_35_7081961" name="code" class="java">class ActivityManagerProxy implements IActivityManager
{
	......

	public void attachApplication(IApplicationThread app) throws RemoteException
	{
		Parcel data = Parcel.obtain();
		Parcel reply = Parcel.obtain();
		data.writeInterfaceToken(IActivityManager.descriptor);
		data.writeStrongBinder(app.asBinder());
		mRemote.transact(ATTACH_APPLICATION_TRANSACTION, data, reply, 0);
		reply.readException();
		data.recycle();
		reply.recycle();
	}

	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; 这里通过Binder驱动程序，最后进入ActivityManagerService的attachApplication函数中。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; Step 26.&nbsp;ActivityManagerService.attachApplication</p><p>&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_36_5271817"  code_snippet_id="127987" snippet_file_name="blog_20131225_36_5271817" name="code" class="java">public final class ActivityManagerService extends ActivityManagerNative
		implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {

	......

	public final void attachApplication(IApplicationThread thread) {
		synchronized (this) {
			int callingPid = Binder.getCallingPid();
			final long origId = Binder.clearCallingIdentity();
			attachApplicationLocked(thread, callingPid);
			Binder.restoreCallingIdentity(origId);
		}
	}

	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这里将操作转发给attachApplicationLocked函数。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 27.&nbsp;ActivityManagerService.attachApplicationLocked</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_37_3461674"  code_snippet_id="127987" snippet_file_name="blog_20131225_37_3461674" name="code" class="java">public final class ActivityManagerService extends ActivityManagerNative
		implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {

	......

	private final boolean attachApplicationLocked(IApplicationThread thread,
			int pid) {
		// Find the application record that is being attached...  either via
		// the pid if we are running in multiple processes, or just pull the
		// next app record if we are emulating process with anonymous threads.
		ProcessRecord app;
		if (pid != MY_PID &amp;&amp; pid &gt;= 0) {
			synchronized (mPidsSelfLocked) {
				app = mPidsSelfLocked.get(pid);
			}
		} else if (mStartingProcesses.size() &gt; 0) {
			......
		} else {
			......
		}

		if (app == null) {
			......
			return false;
		}

		......

		String processName = app.processName;
		try {
			thread.asBinder().linkToDeath(new AppDeathRecipient(
				app, pid, thread), 0);
		} catch (RemoteException e) {
			......
			return false;
		}

		......

		app.thread = thread;
		app.curAdj = app.setAdj = -100;
		app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
		app.setSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
		app.forcingToForeground = null;
		app.foregroundServices = false;
		app.debugging = false;

		......

		boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);

		......

		boolean badApp = false;
		boolean didSomething = false;

		// See if the top visible activity is waiting to run in this process...
		ActivityRecord hr = mMainStack.topRunningActivityLocked(null);
		if (hr != null &amp;&amp; normalMode) {
			if (hr.app == null &amp;&amp; app.info.uid == hr.info.applicationInfo.uid
				&amp;&amp; processName.equals(hr.processName)) {
					try {
						if (mMainStack.realStartActivityLocked(hr, app, true, true)) {
							didSomething = true;
						}
					} catch (Exception e) {
						......
					}
			} else {
				......
			}
		}

		......

		return true;
	}

	......

}</pre><p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在前面的Step 23中，已经创建了一个ProcessRecord，这里首先通过pid将它取回来，放在app变量中，然后对app的其它成员进行初始化，最后调用mMainStack.realStartActivityLocked执行真正的Activity启动操作。这里要启动的Activity通过调用mMainStack.topRunningActivityLocked(null)从堆栈顶端取回来，这时候在堆栈顶端的Activity就是MainActivity了。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 28. ActivityStack.realStartActivityLocked</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_38_6983684"  code_snippet_id="127987" snippet_file_name="blog_20131225_38_6983684" name="code" class="java">public class ActivityStack {

	......

	final boolean realStartActivityLocked(ActivityRecord r,
			ProcessRecord app, boolean andResume, boolean checkConfig)
			throws RemoteException {
		
		......

		r.app = app;

		......

		int idx = app.activities.indexOf(r);
		if (idx &lt; 0) {
			app.activities.add(r);
		}
		
		......

		try {
			......

			List&lt;ResultInfo&gt; results = null;
			List&lt;Intent&gt; newIntents = null;
			if (andResume) {
				results = r.results;
				newIntents = r.newIntents;
			}
	
			......
			
			app.thread.scheduleLaunchActivity(new Intent(r.intent), r,
				System.identityHashCode(r),
				r.info, r.icicle, results, newIntents, !andResume,
				mService.isNextTransitionForward());

			......

		} catch (RemoteException e) {
			......
		}

		......

		return true;
	}

	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这里最终通过app.thread进入到ApplicationThreadProxy的scheduleLaunchActivity函数中，注意，这里的第二个参数r，是一个ActivityRecord类型的Binder对象，用来作来这个Activity的token值。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 29.&nbsp;ApplicationThreadProxy.scheduleLaunchActivity<br />&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在frameworks/base/core/java/android/app/ApplicationThreadNative.java文件中：<br /></p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_39_3221694"  code_snippet_id="127987" snippet_file_name="blog_20131225_39_3221694" name="code" class="java">class ApplicationThreadProxy implements IApplicationThread {

	......

	public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
			ActivityInfo info, Bundle state, List&lt;ResultInfo&gt; pendingResults,
			List&lt;Intent&gt; pendingNewIntents, boolean notResumed, boolean isForward)
			throws RemoteException {
		Parcel data = Parcel.obtain();
		data.writeInterfaceToken(IApplicationThread.descriptor);
		intent.writeToParcel(data, 0);
		data.writeStrongBinder(token);
		data.writeInt(ident);
		info.writeToParcel(data, 0);
		data.writeBundle(state);
		data.writeTypedList(pendingResults);
		data.writeTypedList(pendingNewIntents);
		data.writeInt(notResumed ? 1 : 0);
		data.writeInt(isForward ? 1 : 0);
		mRemote.transact(SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION, data, null,
			IBinder.FLAG_ONEWAY);
		data.recycle();
	}

	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数最终通过Binder驱动程序进入到ApplicationThread的scheduleLaunchActivity函数中。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 30.&nbsp;ApplicationThread.scheduleLaunchActivity<br />&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_40_4999600"  code_snippet_id="127987" snippet_file_name="blog_20131225_40_4999600" name="code" class="java">public final class ActivityThread {

	......

	private final class ApplicationThread extends ApplicationThreadNative {

		......

		// we use token to identify this activity without having to send the
		// activity itself back to the activity manager. (matters more with ipc)
		public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
				ActivityInfo info, Bundle state, List&lt;ResultInfo&gt; pendingResults,
				List&lt;Intent&gt; pendingNewIntents, boolean notResumed, boolean isForward) {
			ActivityClientRecord r = new ActivityClientRecord();

			r.token = token;
			r.ident = ident;
			r.intent = intent;
			r.activityInfo = info;
			r.state = state;

			r.pendingResults = pendingResults;
			r.pendingIntents = pendingNewIntents;

			r.startsNotResumed = notResumed;
			r.isForward = isForward;

			queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
		}

		......

	}

	......
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; 函数首先创建一个ActivityClientRecord实例，并且初始化它的成员变量，然后调用ActivityThread类的queueOrSendMessage函数进一步处理。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; Step 31.&nbsp;ActivityThread.queueOrSendMessage<br />&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_41_124795"  code_snippet_id="127987" snippet_file_name="blog_20131225_41_124795" name="code" class="java">public final class ActivityThread {

	......

	private final class ApplicationThread extends ApplicationThreadNative {

		......

		// if the thread hasn't started yet, we don't have the handler, so just
		// save the messages until we're ready.
		private final void queueOrSendMessage(int what, Object obj) {
			queueOrSendMessage(what, obj, 0, 0);
		}

		......

		private final void queueOrSendMessage(int what, Object obj, int arg1, int arg2) {
			synchronized (this) {
				......
				Message msg = Message.obtain();
				msg.what = what;
				msg.obj = obj;
				msg.arg1 = arg1;
				msg.arg2 = arg2;
				mH.sendMessage(msg);
			}
		}

		......

	}

	......
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;函数把消息内容放在msg中，然后通过mH把消息分发出去，这里的成员变量mH我们在前面已经见过，消息分发出去后，最后会调用H类的handleMessage函数。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 32. H.handleMessage</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中：<br /></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_42_1694958"  code_snippet_id="127987" snippet_file_name="blog_20131225_42_1694958" name="code" class="java">public final class ActivityThread {

	......

	private final class H extends Handler {

		......

		public void handleMessage(Message msg) {
			......
			switch (msg.what) {
			case LAUNCH_ACTIVITY: {
				ActivityClientRecord r = (ActivityClientRecord)msg.obj;

				r.packageInfo = getPackageInfoNoCheck(
					r.activityInfo.applicationInfo);
				handleLaunchActivity(r, null);
			} break;
			......
			}

		......

	}

	......
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这里最后调用ActivityThread类的handleLaunchActivity函数进一步处理。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 33.&nbsp;ActivityThread.handleLaunchActivity</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_43_9884813"  code_snippet_id="127987" snippet_file_name="blog_20131225_43_9884813" name="code" class="java">public final class ActivityThread {

	......

	private final void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
		......

		Activity a = performLaunchActivity(r, customIntent);

		if (a != null) {
			r.createdConfig = new Configuration(mConfiguration);
			Bundle oldState = r.state;
			handleResumeActivity(r.token, false, r.isForward);

			......
		} else {
			......
		}
	}

	......
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这里首先调用performLaunchActivity函数来加载这个Activity类，即shy.luo.activity.MainActivity，然后调用它的onCreate函数，最后回到handleLaunchActivity函数时，再调用handleResumeActivity函数来使这个Activity进入Resumed状态，即会调用这个Activity的onResume函数，这是遵循Activity的生命周期的。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 34.&nbsp;ActivityThread.performLaunchActivity<br />&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_44_582926"  code_snippet_id="127987" snippet_file_name="blog_20131225_44_582926" name="code" class="java">public final class ActivityThread {

	......

	private final Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
		
		ActivityInfo aInfo = r.activityInfo;
		if (r.packageInfo == null) {
			r.packageInfo = getPackageInfo(aInfo.applicationInfo,
				Context.CONTEXT_INCLUDE_CODE);
		}

		ComponentName component = r.intent.getComponent();
		if (component == null) {
			component = r.intent.resolveActivity(
				mInitialApplication.getPackageManager());
			r.intent.setComponent(component);
		}

		if (r.activityInfo.targetActivity != null) {
			component = new ComponentName(r.activityInfo.packageName,
				r.activityInfo.targetActivity);
		}

		Activity activity = null;
		try {
			java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
			activity = mInstrumentation.newActivity(
				cl, component.getClassName(), r.intent);
			r.intent.setExtrasClassLoader(cl);
			if (r.state != null) {
				r.state.setClassLoader(cl);
			}
		} catch (Exception e) {
			......
		}

		try {
			Application app = r.packageInfo.makeApplication(false, mInstrumentation);

			......

			if (activity != null) {
				ContextImpl appContext = new ContextImpl();
				appContext.init(r.packageInfo, r.token, this);
				appContext.setOuterContext(activity);
				CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
				Configuration config = new Configuration(mConfiguration);
				......
				activity.attach(appContext, this, getInstrumentation(), r.token,
					r.ident, app, r.intent, r.activityInfo, title, r.parent,
					r.embeddedID, r.lastNonConfigurationInstance,
					r.lastNonConfigurationChildInstances, config);

				if (customIntent != null) {
					activity.mIntent = customIntent;
				}
				r.lastNonConfigurationInstance = null;
				r.lastNonConfigurationChildInstances = null;
				activity.mStartedActivity = false;
				int theme = r.activityInfo.getThemeResource();
				if (theme != 0) {
					activity.setTheme(theme);
				}

				activity.mCalled = false;
				mInstrumentation.callActivityOnCreate(activity, r.state);
				......
				r.activity = activity;
				r.stopped = true;
				if (!r.activity.mFinished) {
					activity.performStart();
					r.stopped = false;
				}
				if (!r.activity.mFinished) {
					if (r.state != null) {
						mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
					}
				}
				if (!r.activity.mFinished) {
					activity.mCalled = false;
					mInstrumentation.callActivityOnPostCreate(activity, r.state);
					if (!activity.mCalled) {
						throw new SuperNotCalledException(
							&quot;Activity &quot; + r.intent.getComponent().toShortString() +
							&quot; did not call through to super.onPostCreate()&quot;);
					}
				}
			}
			r.paused = true;

			mActivities.put(r.token, r);

		} catch (SuperNotCalledException e) {
			......

		} catch (Exception e) {
			......
		}

		return activity;
	}

	......
}</pre><p>&nbsp;&nbsp; &nbsp; &nbsp; 函数前面是收集要启动的Activity的相关信息，主要package和component信息：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_45_375965"  code_snippet_id="127987" snippet_file_name="blog_20131225_45_375965" name="code" class="java">   ActivityInfo aInfo = r.activityInfo;
   if (r.packageInfo == null) {
        r.packageInfo = getPackageInfo(aInfo.applicationInfo,
                Context.CONTEXT_INCLUDE_CODE);
   }

   ComponentName component = r.intent.getComponent();
   if (component == null) {
       component = r.intent.resolveActivity(
           mInitialApplication.getPackageManager());
       r.intent.setComponent(component);
   }

   if (r.activityInfo.targetActivity != null) {
       component = new ComponentName(r.activityInfo.packageName,
               r.activityInfo.targetActivity);
   }</pre>&nbsp;&nbsp; &nbsp; &nbsp; 然后通过ClassLoader将shy.luo.activity.MainActivity类加载进来：<p></p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_46_6057566"  code_snippet_id="127987" snippet_file_name="blog_20131225_46_6057566" name="code" class="java">   Activity activity = null;
   try {
	java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
	activity = mInstrumentation.newActivity(
		cl, component.getClassName(), r.intent);
	r.intent.setExtrasClassLoader(cl);
	if (r.state != null) {
		r.state.setClassLoader(cl);
	}
   } catch (Exception e) {
	......
   }</pre>&nbsp;&nbsp; &nbsp; &nbsp;接下来是创建Application对象，这是根据AndroidManifest.xml配置文件中的Application标签的信息来创建的：<p></p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_47_5883626"  code_snippet_id="127987" snippet_file_name="blog_20131225_47_5883626" name="code" class="java">   Application app = r.packageInfo.makeApplication(false, mInstrumentation);</pre>&nbsp;&nbsp; &nbsp; &nbsp;后面的代码主要创建Activity的上下文信息，并通过attach方法将这些上下文信息设置到MainActivity中去：<p></p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_48_4073482"  code_snippet_id="127987" snippet_file_name="blog_20131225_48_4073482" name="code" class="java">   activity.attach(appContext, this, getInstrumentation(), r.token,
	r.ident, app, r.intent, r.activityInfo, title, r.parent,
	r.embeddedID, r.lastNonConfigurationInstance,
	r.lastNonConfigurationChildInstances, config);</pre>&nbsp;&nbsp; &nbsp; &nbsp;最后还要调用MainActivity的onCreate函数：<p></p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_49_8675288"  code_snippet_id="127987" snippet_file_name="blog_20131225_49_8675288" name="code" class="java">   mInstrumentation.callActivityOnCreate(activity, r.state);</pre>&nbsp;&nbsp; &nbsp; &nbsp;这里不是直接调用MainActivity的onCreate函数，而是通过mInstrumentation的callActivityOnCreate函数来间接调用，前面我们说过，mInstrumentation在这里的作用是监控Activity与系统的交互操作，相当于是系统运行日志。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp;Step 35. MainActivity.onCreate</p><p>&nbsp;&nbsp; &nbsp; &nbsp;这个函数定义在packages/experimental/Activity/src/shy/luo/activity/MainActivity.java文件中，这是我们自定义的app工程文件：</p><p></p><pre code_snippet_id="127987" snippet_file_name="blog_20131225_50_6308737"  code_snippet_id="127987" snippet_file_name="blog_20131225_50_6308737" name="code" class="java">public class MainActivity extends Activity  implements OnClickListener {
	
	......

	@Override
	public void onCreate(Bundle savedInstanceState) {
		......

		Log.i(LOG_TAG, &quot;Main Activity Created.&quot;);
	}

	......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; 这样，MainActivity就启动起来了，整个应用程序也启动起来了。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; 整个应用程序的启动过程要执行很多步骤，但是整体来看，主要分为以下五个阶段：</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 一. Step1 - Step 11：Launcher通过Binder进程间通信机制通知ActivityManagerService，它要启动一个Activity；</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 二. Step 12 - Step 16：ActivityManagerService通过Binder进程间通信机制通知Launcher进入Paused状态；</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 三. Step 17 - Step 24：Launcher通过Binder进程间通信机制通知ActivityManagerService，它已经准备就绪进入Paused状态，于是ActivityManagerService就创建一个新的进程，用来启动一个ActivityThread实例，即将要启动的Activity就是在这个ActivityThread实例中运行；</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 四. Step 25 - Step 27：ActivityThread通过Binder进程间通信机制将一个ApplicationThread类型的Binder对象传递给ActivityManagerService，以便以后ActivityManagerService能够通过这个Binder对象和它进行通信；</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 五. Step 28 - Step 35：ActivityManagerService通过Binder进程间通信机制通知ActivityThread，现在一切准备就绪，它可以真正执行Activity的启动操作了。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 这里不少地方涉及到了Binder进程间通信机制，相关资料请参考<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/6618363">Android进程间通信（IPC）机制Binder简要介绍和学习计划</a>一文。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 这样，应用程序的启动过程就介绍完了，它实质上是启动应用程序的默认Activity，在下一篇文章中，我们将介绍在应用程序内部启动另一个Activity的过程，即新的Activity与启动它的Activity将会在同一个进程（Process）和任务（Task）运行，敬请关注。</p><p style="text-align: center;"><span style="font-size:14px;color:#cc0000;"><strong>老罗的新浪微博：<a target=_blank target="_blank" href="http://weibo.com/shengyangluo">http://weibo.com/shengyangluo</a>，欢迎关注！</strong></span><br /></p>
</div>



<!-- Baidu Button BEGIN -->
<div id="bdshare" class="bdshare_t bds_tools get-codes-bdshare" style="float: right;">
<a class="bds_qzone"></a>
<a class="bds_tsina"></a>
<a class="bds_tqq"></a>
<a class="bds_renren"></a>
<a class="bds_t163"></a>
<span class="bds_more">更多</span>
<a class="shareCount"></a>
</div>
<!-- Baidu Button END -->


<!--192.168.100.34-->
<div class="article_next_prev">
            <li class="prev_article"><span>上一篇：</span><a href="/luoshengyang/article/details/6685853" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_shangyipian'])">Android应用程序的Activity启动过程简要介绍和学习计划</a></li>
            <li class="next_article"><span>下一篇：</span><a href="/luoshengyang/article/details/6703247" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_xiayipian'])">Android应用程序内部启动Activity过程（startActivity）的源代码分析</a></li>
</div>

<!-- Baidu Button BEGIN -->
<script type="text/javascript" id="bdshare_js" data="type=tools&amp;uid=1536434" ></script>
<script type="text/javascript" id="bdshell_js"></script>
<script type="text/javascript">
    document.getElementById("bdshell_js").src = "http://bdimg.share.baidu.com/static/js/shell_v2.js?cdnversion=" + Math.ceil(new Date()/3600000)
</script>
<!-- Baidu Button END -->

        <div id="digg" ArticleId="6689748">
            <dl id="btnDigg" class="digg digg_disable">
                <dt onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_ding'])">顶</dt>
                <dd>69</dd>
            </dl>
            <dl id="btnBury" class="digg digg_disable">
                <dt onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_cai'])">踩</dt>
                <dd>6</dd>
            </dl>
        </div>
</div>
      <dl class="blog-associat-tag">
        <dt>相关主题推荐</dt>
        <dd>
                <a href="http://www.csdn.net/tag/android应用" target="_blank">android应用</a> 
                <a href="http://www.csdn.net/tag/新浪微博" target="_blank">新浪微博</a> 
                <a href="http://www.csdn.net/tag/源代码" target="_blank">源代码</a> 
                <a href="http://www.csdn.net/tag/classloader" target="_blank">classloader</a> 
                <a href="http://www.csdn.net/tag/something" target="_blank">something</a> 
        </dd>
    </dl> 
    <dl class="blog-ass-articl">
        <dt>相关博文推荐</dt>
            <dd>
                <a href="http://blog.csdn.net/u012385733/article/details/21110771" title="等待队列源码分析" target="_blank">等待队列源码分析</a></dd>
            <dd>
                <a href="http://blog.csdn.net/liliangzhao/article/details/21104933" title="20140309技术交流活动总结" target="_blank">20140309技术交流活动总结</a></dd>
            <dd>
                <a href="http://blog.csdn.net/u011278496/article/details/21102055" title="Joda-Time 工具集简介-使用" target="_blank">Joda-Time 工具集简介-使用</a></dd>
            <dd>
                <a href="http://blog.csdn.net/lechengyuyuan/article/details/21092461" title="Android 源码环境搭建配置与问题解析（二）" target="_blank">Android 源码环境搭建配置与问题解...</a></dd>
            <dd>
                <a href="http://blog.csdn.net/ymq_2012/article/details/21086365" title="开发计算器源代码" target="_blank">开发计算器源代码</a></dd>
            <dd>
                <a href="http://blog.csdn.net/u013782762/article/details/21083817" title="fn.js的源码欣赏(2)" target="_blank">fn.js的源码欣赏(2)</a></dd>
            <dd>
                <a href="http://blog.csdn.net/u013782762/article/details/21081415" title="fn.js的源码欣赏(1)" target="_blank">fn.js的源码欣赏(1)</a></dd>
            <dd>
                <a href="http://blog.csdn.net/boyleeht/article/details/21053749" title="【leetcode题解】5 - Surrounded Regions" target="_blank">【leetcode题解】5 - Surr...</a></dd>
    </dl>

    <div id="ad_cen">
        <script type="text/javascript">            BAIDU_CLB_SLOT_ID = "117306";</script>
        <script type="text/javascript" src="http://cbjs.baidu.com/js/o.js"></script>
    </div>
    <script type="text/javascript">
        //new Ad(4, 'ad_cen');
    </script>
<div id="comment_title" class="panel_head">
    查看评论<a name="comments"></a></div>
<div id="comment_list">
</div>
<div id="comment_bar">
</div>
<div id="comment_form">
</div>
<div class="announce">
    * 以上用户言论只代表其个人观点，不代表CSDN网站的观点或立场<a name="reply"></a><a name="quote"></a></div>
<script type="text/javascript">
    var fileName = '6689748';
    var commentscount = 322;
    var islock = false
</script>
<script type="text/javascript" src="http://static.blog.csdn.net/scripts/comment.js"></script>
    <div id="ad_bot">
    </div>
    <script type="text/javascript">
    new Ad(5, 'ad_bot');
    </script>
<div id="report_dialog">
</div>
<div id="d-top" style="display: none;">
    <a id="d-top-a" href="#" title="回到顶部" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_huidaodingbu'])">
        <img src="http://static.blog.csdn.net/images/top.png" alt="TOP" /></a>
</div>
<script type="text/javascript">
    $(function ()
    {
        var d_top = $('#d-top');
        document.onscroll = function ()
        {
            var scrTop = (document.body.scrollTop || document.documentElement.scrollTop);
            if (scrTop > 500)
            {
                d_top.show();
            } else
            {
                d_top.hide();
            }
        }
        $('#d-top-a').click(function ()
        {
            scrollTo(0, 0);
            this.blur();
            return false;
        });
    });
  
</script>
<style type="text/css">
    .tag_list
    {
        background: none repeat scroll 0 0 #FFFFFF;
        border: 1px solid #D7CBC1;
        color: #000000;
        font-size: 12px;
        line-height: 20px;
        list-style: none outside none;
        margin: 10px 2% 0 1%;
        padding: 1px;
    }
    .tag_list h5
    {
        background: none repeat scroll 0 0 #E0DBD3;
        color: #47381C;
        font-size: 12px;
        height: 24px;
        line-height: 24px;
        padding: 0 5px;
        margin: 0;
    }
    .tag_list h5 a
    {
        color: #47381C;
    }
    .classify
    {
        margin: 10px 0;
        padding: 4px 12px 8px;
    }
    .classify a
    {
        margin-right: 20px;
        white-space: nowrap;
    }
</style>
<div class="tag_list">
    <h5>
        <a href="http://www.csdn.net/tag/" target="_blank">核心技术类目</a></h5>
    <div class="classify">
<a title="全部主题" href="http://www.csdn.net/tag" target="_blank" onclick="LogClickCount(this,336);">全部主题</a>
<a title="Java" href="http://www.csdn.net/tag/Java" target="_blank" onclick="LogClickCount(this,336);">Java</a>
<a title="VPN" href="http://www.csdn.net/tag/vpn" target="_blank" onclick="LogClickCount(this,336);">VPN</a>
<a title="Android" href="http://www.csdn.net/tag/android" target="_blank" onclick="LogClickCount(this,336);">Android</a>
<a title="iOS" href="http://www.csdn.net/tag/ios" target="_blank" onclick="LogClickCount(this,336);">iOS</a>
<a title="ERP" href="http://www.csdn.net/tag/erp" target="_blank" onclick="LogClickCount(this,336);">ERP</a>
<a title="IE10" href="http://www.csdn.net/tag/ie10" target="_blank" onclick="LogClickCount(this,336);">IE10</a>
<a title="Eclipse" href="http://www.csdn.net/tag/eclipse" target="_blank" onclick="LogClickCount(this,336);">Eclipse</a>
<a title="CRM" href="http://www.csdn.net/tag/crm" target="_blank" onclick="LogClickCount(this,336);">CRM</a>
<a title="JavaScript" href="http://www.csdn.net/tag/javascript" target="_blank" onclick="LogClickCount(this,336);">JavaScript</a>
<a title="Ubuntu" href="http://www.csdn.net/tag/ubuntu" target="_blank" onclick="LogClickCount(this,336);">Ubuntu</a>
<a title="NFC" href="http://www.csdn.net/tag/nfc" target="_blank" onclick="LogClickCount(this,336);">NFC</a>
<a title="WAP" href="http://www.csdn.net/tag/wap" target="_blank" onclick="LogClickCount(this,336);">WAP</a>
<a title="jQuery" href="http://www.csdn.net/tag/jquery" target="_blank" onclick="LogClickCount(this,336);">jQuery</a>
<a title="数据库" href="http://www.csdn.net/tag/数据库" target="_blank" onclick="LogClickCount(this,336);">数据库</a>
<a title="BI" href="http://www.csdn.net/tag/bi" target="_blank" onclick="LogClickCount(this,336);">BI</a>
<a title="HTML5" href="http://www.csdn.net/tag/html5" target="_blank" onclick="LogClickCount(this,336);">HTML5</a>
<a title="Spring" href="http://www.csdn.net/tag/spring" target="_blank" onclick="LogClickCount(this,336);">Spring</a>
<a title="Apache" href="http://www.csdn.net/tag/apache" target="_blank" onclick="LogClickCount(this,336);">Apache</a>
<a title="Hadoop" href="http://www.csdn.net/tag/hadoop" target="_blank" onclick="LogClickCount(this,336);">Hadoop</a>
<a title=".NET" href="http://www.csdn.net/tag/.net" target="_blank" onclick="LogClickCount(this,336);">.NET</a>
<a title="API" href="http://www.csdn.net/tag/api" target="_blank" onclick="LogClickCount(this,336);">API</a>
<a title="HTML" href="http://www.csdn.net/tag/html" target="_blank" onclick="LogClickCount(this,336);">HTML</a>
<a title="SDK" href="http://www.csdn.net/tag/sdk" target="_blank" onclick="LogClickCount(this,336);">SDK</a>
<a title="IIS" href="http://www.csdn.net/tag/iis" target="_blank" onclick="LogClickCount(this,336);">IIS</a>
<a title="Fedora" href="http://www.csdn.net/tag/fedora" target="_blank" onclick="LogClickCount(this,336);">Fedora</a>
<a title="XML" href="http://www.csdn.net/tag/xml" target="_blank" onclick="LogClickCount(this,336);">XML</a>
<a title="LBS" href="http://www.csdn.net/tag/lbs" target="_blank" onclick="LogClickCount(this,336);">LBS</a>
<a title="Unity" href="http://www.csdn.net/tag/unity" target="_blank" onclick="LogClickCount(this,336);">Unity</a>
<a title="Splashtop" href="http://www.csdn.net/tag/splashtop" target="_blank" onclick="LogClickCount(this,336);">Splashtop</a>
<a title="UML" href="http://www.csdn.net/tag/uml" target="_blank" onclick="LogClickCount(this,336);">UML</a>
<a title="components" href="http://www.csdn.net/tag/components" target="_blank" onclick="LogClickCount(this,336);">components</a>
<a title="Windows Mobile" href="http://www.csdn.net/tag/windowsmobile" target="_blank" onclick="LogClickCount(this,336);">Windows Mobile</a>
<a title="Rails" href="http://www.csdn.net/tag/rails" target="_blank" onclick="LogClickCount(this,336);">Rails</a>
<a title="QEMU" href="http://www.csdn.net/tag/qemu" target="_blank" onclick="LogClickCount(this,336);">QEMU</a>
<a title="KDE" href="http://www.csdn.net/tag/kde" target="_blank" onclick="LogClickCount(this,336);">KDE</a>
<a title="Cassandra" href="http://www.csdn.net/tag/cassandra" target="_blank" onclick="LogClickCount(this,336);">Cassandra</a>
<a title="CloudStack" href="http://www.csdn.net/tag/cloudstack" target="_blank" onclick="LogClickCount(this,336);">CloudStack</a>
<a title="FTC" href="http://www.csdn.net/tag/ftc" target="_blank" onclick="LogClickCount(this,336);">FTC</a>
<a title="coremail" href="http://www.csdn.net/tag/coremail" target="_blank" onclick="LogClickCount(this,336);">coremail</a>
<a title="OPhone " href="http://www.csdn.net/tag/ophone " target="_blank" onclick="LogClickCount(this,336);">OPhone </a>
<a title="CouchBase" href="http://www.csdn.net/tag/couchbase" target="_blank" onclick="LogClickCount(this,336);">CouchBase</a>
<a title="云计算" href="http://www.csdn.net/tag/云计算" target="_blank" onclick="LogClickCount(this,336);">云计算</a>
<a title="iOS6" href="http://www.csdn.net/tag/iOS6" target="_blank" onclick="LogClickCount(this,336);">iOS6</a>
<a title="Rackspace " href="http://www.csdn.net/tag/rackspace " target="_blank" onclick="LogClickCount(this,336);">Rackspace </a>
<a title="Web App" href="http://www.csdn.net/tag/webapp" target="_blank" onclick="LogClickCount(this,336);">Web App</a>
<a title="SpringSide" href="http://www.csdn.net/tag/springside" target="_blank" onclick="LogClickCount(this,336);">SpringSide</a>
<a title="Maemo" href="http://www.csdn.net/tag/maemo" target="_blank" onclick="LogClickCount(this,336);">Maemo</a>
<a title="Compuware" href="http://www.csdn.net/tag/compuware" target="_blank" onclick="LogClickCount(this,336);">Compuware</a>
<a title="大数据" href="http://www.csdn.net/tag/大数据" target="_blank" onclick="LogClickCount(this,336);">大数据</a>
<a title="aptech" href="http://www.csdn.net/tag/aptech" target="_blank" onclick="LogClickCount(this,336);">aptech</a>
<a title="Perl" href="http://www.csdn.net/tag/perl" target="_blank" onclick="LogClickCount(this,336);">Perl</a>
<a title="Tornado" href="http://www.csdn.net/tag/tornado" target="_blank" onclick="LogClickCount(this,336);">Tornado</a>
<a title="Ruby" href="http://www.csdn.net/tag/ruby" target="_blank" onclick="LogClickCount(this,336);">Ruby</a>
<a title="Hibernate" href="http://www.csdn.net/hibernate" target="_blank" onclick="LogClickCount(this,336);">Hibernate</a>
<a title="ThinkPHP" href="http://www.csdn.net/tag/thinkphp" target="_blank" onclick="LogClickCount(this,336);">ThinkPHP</a>
<a title="Spark" href="http://www.csdn.net/tag/spark" target="_blank" onclick="LogClickCount(this,336);">Spark</a>
<a title="HBase" href="http://www.csdn.net/tag/hbase" target="_blank" onclick="LogClickCount(this,336);">HBase</a>
<a title="Pure" href="http://www.csdn.net/tag/pure" target="_blank" onclick="LogClickCount(this,336);">Pure</a>
<a title="Solr" href="http://www.csdn.net/tag/solr" target="_blank" onclick="LogClickCount(this,336);">Solr</a>
<a title="Angular" href="http://www.csdn.net/tag/angular" target="_blank" onclick="LogClickCount(this,336);">Angular</a>
<a title="Cloud Foundry" href="http://www.csdn.net/tag/cloudfoundry" target="_blank" onclick="LogClickCount(this,336);">Cloud Foundry</a>
<a title="Redis" href="http://www.csdn.net/tag/redis" target="_blank" onclick="LogClickCount(this,336);">Redis</a>
<a title="Scala" href="http://www.csdn.net/tag/scala" target="_blank" onclick="LogClickCount(this,336);">Scala</a>
<a title="Django" href="http://www.csdn.net/tag/django" target="_blank" onclick="LogClickCount(this,336);">Django</a>
<a title="Bootstrap" href="http://www.csdn.net/tag/bootstrap" target="_blank" onclick="LogClickCount(this,336);">Bootstrap</a>
    </div>
</div>

                    <div class="clear">
                    </div>
                </div>
            </div>
            <div id="side">
    <div class="side">
<div id="panel_Profile" class="panel">
<ul class="panel_head"><span>个人资料</span></ul>
<ul class="panel_body profile">
<div id="blog_userface">
    <a href="http://my.csdn.net/Luoshengyang" target="_blank">
    <img src="http://avatar.csdn.net/5/6/E/1_luoshengyang.jpg" title="访问我的空间" style="max-width:90%"/>
    </a>
    <br />
    <span><a href="http://my.csdn.net/Luoshengyang" class="user_name" target="_blank">Luoshengyang</a></span>
</div>
<div class="interact">
<a href="javascript:void(0);" class="attent" id="span_add_follow" title="[加关注]" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_guanzhu'])"></a>
<a href="javascript:void(0);" class="letter" onclick="loginto(1)" title="[发私信]" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_sixin'])"></a>
</div>
<div id="blog_medal">
        <div class="ico_expert" title="CSDN认证专家"></div>
</div>
<ul id="blog_rank">
    <li>访问：<span>3159541次</span></li>
    <li>积分：<span>16447分</span></li>
    <li>排名：<span>第129名</span></li>
</ul>
<ul id="blog_statistics">
    <li>原创：<span>100篇</span></li>
    <li>转载：<span>0篇</span></li>
    <li>译文：<span>0篇</span></li>
    <li>评论：<span>5212条</span></li>
</ul>
</ul>
</div>


<div id="custom_column_6567056" class="panel">
<ul class="panel_head"><span>博客公告</span></ul>
<ul class="panel_body">
<b><font color="red" size="2">本博客所有文章均为原创，欢迎交流，欢迎转载；转载请勿篡改内容，并且注明出处，禁止用于商业目的，谢谢！</font></b>
</ul>
</div><div id="custom_column_21664874" class="panel">
<ul class="panel_head"><span>图书信息</span></ul>
<ul class="panel_body">
<font color="red"><b>书名：</b></font>
<p>《Android系统源代码情景分析》</p>

<p><a href="http://wenku.it168.com/d_000574719.shtml" target="_blank">试读请点击</a></p>

<p><font color="red"><b>出版社：</b></font></p>
<p>电子工业出版社</p>

<p><font color="red"><b>网店：</b></font></p>

<p>1. <a href="http://product.dangdang.com/product.aspx?product_id=22884086" target="_blank">当当网（满100减20，满200减50）</a></p><p></p>

<p>2. <a href="http://book.360buy.com/11104739.html" target="_blank">京东网（满99减20）</a></p><p></p>

<p>3. <a href="http://www.amazon.cn/Android%E7%B3%BB%E7%BB%9F%E6%BA%90%E4%BB%A3%E7%A0%81%E6%83%85%E6%99%AF%E5%88%86%E6%9E%90-%E7%BD%97%E5%8D%87%E9%98%B3/dp/B009OLU8EE" target="_blank">亚马逊网（满100减20，满200减50）</a></p><p></p>

<p>4. <a href="http://product.china-pub.com/3684413" target="_blank">互动出版网</a></p><p></p>

<p>5. <a href="http://www.suning.com/emall/prd_10052_22001_-7_3435777_.html" target="_blank">苏宁易购网</a></p><p></p>

<p><a href="http://s.etao.com/search?spm=1002.8.1.1386.g76Rk3&amp;q=Android%CF%B5%CD%B3%D4%B4%B4%FA%C2%EB%C7%E9%BE%B0%B7%D6%CE%F6&amp;style=list&amp;initiative_id=etao_20121011&amp;pn=1&amp;sort=sale-desc" target="_blank">比一比谁更实惠</a></p>

<p><font color="red"><b>号外：</b></font></p>

<p>本书繁体版已经成功输出到台湾</p>

<p></p>
</ul>
</div><div id="custom_column_21603386" class="panel">
<ul class="panel_head"><span>联系方式</span></ul>
<ul class="panel_body">
<font color="red"><b>新浪微博：</b></font>
<p><a href="http://weibo.com/shengyangluo" target="_blank"><font color="red"><b>http://weibo.com/shengyangluo</b></font></a></p><p></p>

<font color="red"><b>QQ交流群：</b></font>
<p><b><font color="red">130112760</font></b>（1000人群，已满）</p>
<p><b><font color="red">248749286</font></b>（1000人群，已满）</p>
<p><b><font color="red">204155322</font></b>（500人群，未满）</p>
<p><b><font color="red">278558417</font></b>（500人群，已满）</p>
<p><b><font color="red">303165655</font></b>（500人群，已满）</p>
<p>PS：请勿同时加入多个群，一经发现，永久封号，谢谢！</p>
</ul>
</div><div id="panel_Category" class="panel">
    <ul class="panel_head"><span>博客专栏</span></ul>
    <ul class="panel_body" id="sp_column">
    <table cellpadding="0" cellspacing="0"><tr>
    <td style="padding:10px 10px 0 0;">
    <a href="http://blog.csdn.net/column/details/androidluo.html" target="_blank"><img src="http://avatar.csdn.net/blogpic/20111107095438958.jpg" style="width:75px;height:75px;" /></a>
    </td>
    <td style="padding:10px 0; vertical-align:top;">
    <a href="http://blog.csdn.net/column/details/androidluo.html" target="_blank">老罗的Android之旅</a>
    <p>文章：100篇</p>
    <span>阅读：3163336</span>
    </td>
    </tr></table>
    </ul>
</div><div id="hotarticls" class="panel">
<ul class="panel_head"><span>阅读排行</span></ul>
<ul class="panel_body itemlist">
<li>
<a href="/luoshengyang/article/details/6559955" title="在Ubuntu上下载、编译和安装Android最新源代码">在Ubuntu上下载、编译和安装Android最新源代码</a><span>(109317)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6618363" title="Android进程间通信（IPC）机制Binder简要介绍和学习计划">Android进程间通信（IPC）机制Binder简要介绍和学习计划</a><span>(95185)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6689748" title="Android应用程序启动过程源代码分析">Android应用程序启动过程源代码分析</a><span>(80750)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6568411" title="在Ubuntu上为Android系统编写Linux内核驱动程序">在Ubuntu上为Android系统编写Linux内核驱动程序</a><span>(78561)</span>
</li>
<li>
<a href="/luoshengyang/article/details/8923485" title="那两年炼就的Android内功修养">那两年炼就的Android内功修养</a><span>(76460)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6567257" title="Android硬件抽象层（HAL）概要介绍和学习计划">Android硬件抽象层（HAL）概要介绍和学习计划</a><span>(72469)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6564592" title="在Ubuntu上下载、编译和安装Android最新内核源代码（Linux Kernel）">在Ubuntu上下载、编译和安装Android最新内核源代码（Linux Kernel）</a><span>(66668)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6557518" title="Android学习启动篇">Android学习启动篇</a><span>(58980)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6566662" title="如何单独编译Android源代码中的模块">如何单独编译Android源代码中的模块</a><span>(57960)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6575988" title="在Ubuntu为Android硬件抽象层（HAL）模块编写JNI方法提供Java访问硬件服务接口">在Ubuntu为Android硬件抽象层（HAL）模块编写JNI方法提供Java访问硬件服务接口</a><span>(56692)</span>
</li>
</ul>
</div>
<div id="hotarticls2" class="panel">
<ul class="panel_head"><span>评论排行</span></ul>
<ul class="panel_body itemlist">
<li>
<a href="/luoshengyang/article/details/6689748" title="Android应用程序启动过程源代码分析">Android应用程序启动过程源代码分析</a><span>(322)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6559955" title="在Ubuntu上下载、编译和安装Android最新源代码">在Ubuntu上下载、编译和安装Android最新源代码</a><span>(238)</span>
</li>
<li>
<a href="/luoshengyang/article/details/8116866" title="《Android系统源代码情景分析》一书勘误">《Android系统源代码情景分析》一书勘误</a><span>(208)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6882903" title="Android应用程序键盘（Keyboard）消息处理机制分析">Android应用程序键盘（Keyboard）消息处理机制分析</a><span>(182)</span>
</li>
<li>
<a href="/luoshengyang/article/details/8923485" title="那两年炼就的Android内功修养">那两年炼就的Android内功修养</a><span>(181)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6580267" title="在Ubuntu上为Android系统内置Java应用程序测试Application Frameworks层的硬件服务">在Ubuntu上为Android系统内置Java应用程序测试Application Frameworks层的硬件服务</a><span>(170)</span>
</li>
<li>
<a href="/luoshengyang/article/details/7409491" title="Android博客文章整理">Android博客文章整理</a><span>(167)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6568411" title="在Ubuntu上为Android系统编写Linux内核驱动程序">在Ubuntu上为Android系统编写Linux内核驱动程序</a><span>(151)</span>
</li>
<li>
<a href="/luoshengyang/article/details/8452527" title="2012年的Android之旅：梦想、学习、坚持、自信、淡定">2012年的Android之旅：梦想、学习、坚持、自信、淡定</a><span>(139)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6564592" title="在Ubuntu上下载、编译和安装Android最新内核源代码（Linux Kernel）">在Ubuntu上下载、编译和安装Android最新内核源代码（Linux Kernel）</a><span>(136)</span>
</li>
</ul>
</div>
<div id="newcomments" class="panel">
<ul class="panel_head"><span>最新评论</span></ul>
<ul class="panel_body itemlist">
    <li>
    <a href="/Luoshengyang/article/details/6568411#comments">在Ubuntu上为Android系统编写Linux内核驱动程序</a>
    <p style="margin:0px;"><a href="/HuiyuYang_fish" class="user_name">HuiyuYang_fish</a>:
期待实验~
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/6557518#comments">Android学习启动篇</a>
    <p style="margin:0px;"><a href="/HuiyuYang_fish" class="user_name">HuiyuYang_fish</a>:
貌似我也要开始android的初步涉猎了~
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/12957169#comments">《老罗的Android之旅》导读PPT</a>
    <p style="margin:0px;"><a href="/mianju23" class="user_name">mianju23</a>:
牛人
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/8452527#comments">2012年的Android之旅：梦想、学习、坚持、自信、淡定</a>
    <p style="margin:0px;"><a href="/wuxiaoming1733" class="user_name">wuxiaoming1733</a>:
顶楼主
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/6737352#comments">Android应用程序注册广播接收器（registerReceiver）的过程分析</a>
    <p style="margin:0px;"><a href="/wuxiaoming1733" class="user_name">wuxiaoming1733</a>:
罗大神你好，我这边还没明白，怎么自己用代码，实现androidmanifest.xml里静态注册系统...
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/6642463#comments">Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析</a>
    <p style="margin:0px;"><a href="/zqz19891031" class="user_name">zqz19891031</a>:
博主您好，看了你的博客，收获颇多！有一个问题想请教：最近在看Android的Binder实现，看到B...
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/8923485#comments">那两年炼就的Android内功修养</a>
    <p style="margin:0px;"><a href="/xin128888" class="user_name">xin128888</a>:
老罗，有问题能否指导下。目前我想学习驱动相关知识，但是不知道要看哪本入门书籍合适，目标主要是快速入门...
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/19023609#comments">Android源代码编译命令m/mm/mmm/make分析</a>
    <p style="margin:0px;"><a href="/soledadzz" class="user_name">soledadzz</a>:
您的文章已被推荐到CSDN首页，感谢您的分享。
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/19023609#comments">Android源代码编译命令m/mm/mmm/make分析</a>
    <p style="margin:0px;"><a href="/u013725065" class="user_name">u013725065</a>:

    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/19023609#comments">Android源代码编译命令m/mm/mmm/make分析</a>
    <p style="margin:0px;"><a href="/u013725065" class="user_name">u013725065</a>:

    </p>
    </li>
</ul>
</div>
    </div>
    <div class="clear">
    </div>
</div>

            <div class="clear">
            </div>
        </div>
        

<script type="text/javascript" src="http://static.blog.csdn.net/scripts/newblog.min.js"></script>
<script type="text/javascript" src="http://medal.blog.csdn.net/showblogmedal.ashx?blogid=1134701"></script>
<script type="text/javascript">
    document.write('<script type="text/javascript" src="http://csdnimg.cn/pubfooter/js/publib_footer.js?' + Math.floor(new Date() / 120000).toString(36) + '="></' + 'script>');
</script>
    <script type="text/javascript" src="http://passport.csdn.net/content/loginbox/login.js"></script>
<script type="text/javascript">document.write("<img src=http://counter.csdn.net/pv.aspx?id=24 border=0 width=0 height=0>");</script>
<script type="text/javascript" src="http://www.csdn.net/ui/scripts/Csdn/counter.js"></script>
<script type="text/javascript" src="http://ad.csdn.net/scripts/ad-blog.js"></script>
<script type="text/javascript" src="http://zz.csdn.net/js/count.js"></script>
<script type="text/javascript">
    $(function () {
        function __get_code_toolbar(snippet_id) {
            return $("<a href='https://code.csdn.net/snippets/"
                    + snippet_id
                    + "' target='_blank' title='在CODE上查看代码片' style='text-indent:0;'><img src='https://code.csdn.net/assets/CODE_ico.png' width=12 height=12 alt='在CODE上查看代码片' style='position:relative;top:1px;left:2px;'/></a>"
                    + "<a href='https://code.csdn.net/snippets/"
                    + snippet_id
                    + "/fork' target='_blank' title='派生到我的代码片'  style='text-indent:0;'><img src='https://code.csdn.net/assets/ico_fork.svg' width=12 height=12 alt='派生到我的代码片' style='position:relative;top:2px;left:2px;'/></a>");
        }
        
        $("[code_snippet_id]").each(function () {
            __s_id = $(this).attr("code_snippet_id");
            if (__s_id != null && __s_id != "" && __s_id != 0 && parseInt(__s_id) > 70020) {
                __code_tool = __get_code_toolbar(__s_id);
                $(this).prev().find(".tools").append(__code_tool);
            }
        });
    });
</script>

    </div>
      <!--new top-->
    
    <script id="csdn-toolbar-id" btnId="header_notice_num" wrapId="note1" count="5" subCount="5" type="text/javascript" src="http://static.csdn.net/public/common/toolbar/js/toolbar.js"></script>     <!--new top-->
</body>
</html>
